// /////////////////////////////////////////////////////////////////////////////
//
// Name:            AnimatedMeshComponent.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Component wrapper of Irrlicht's IAnimatedMeshSceneNode.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "AnimatedMeshComponent.h"
#include "../../core/GameManager.h"


// AnimatedMeshComponent class
// TextComponent constructor (default).
AnimatedMeshComponent::AnimatedMeshComponent(Entity *parent)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
      init();
}

// AnimatedMeshComponent constructor.
AnimatedMeshComponent::AnimatedMeshComponent(Entity *parent, const std::string &fileName,
                                             const vector3df &scale)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init(scale);
       setMesh(fileName);
    }
}

// AnimatedMeshComponent constructor.
AnimatedMeshComponent::AnimatedMeshComponent(Entity *parent, IAnimatedMesh *mesh,
                                             const vector3df &scale)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init(scale);
       setMesh(mesh);
    }
}

// AnimatedMeshComponent deconstructor.
AnimatedMeshComponent::~AnimatedMeshComponent()
{
    if(pParent != NULL)
    {
       mAnimatedMeshSN->remove();
       mSceneNode = NULL;
    }
}

// Common initialisation method.
void AnimatedMeshComponent::init(const vector3df &scale)
{
    // Get pointers to needed sub-systems of the Game Manager.
    ISceneManager *pSceneMgr = GameManager::Instance()->getSceneManager();

    // Create the internal scene node.
    mAnimatedMeshSN = pSceneMgr->addAnimatedMeshSceneNode(0, 0, pParent->getID(),
                                                          pParent->getAbsolutePosition(),
                                                          pParent->getAbsoluteRotation(),
                                                          scale, true);
    mSceneNode = mAnimatedMeshSN;
    mShadowVolumeSN = NULL;

    // Set-up internal mechanism for collision detection.
    mTriSelector = pSceneMgr->createTriangleSelectorFromBoundingBox(mAnimatedMeshSN);
    mMetaSelector = pSceneMgr->createMetaTriangleSelector();
}

// Returns a direct pointer to the IAnimatedMeshSceneNode.
IAnimatedMeshSceneNode* AnimatedMeshComponent::getAnimatedMeshSceneNode()
{
    return mAnimatedMeshSN;
}

// Animates the mesh based upon the current frame.
void AnimatedMeshComponent::animate()
{
    mAnimatedMeshSN->animateJoints();
}

// Returns the current end frame number.
s32 AnimatedMeshComponent::getEndFrame() const
{
    return mAnimatedMeshSN->getEndFrame();
}

// Returns the current displayed frame number.
f32 AnimatedMeshComponent::getFrame() const
{
    return mAnimatedMeshSN->getFrameNr();
}

// Returns the current mesh.
IMesh* AnimatedMeshComponent::getMesh()
{
    return mAnimatedMeshSN->getMesh();
}

// Gets the shadow volume scene node associated with this component.
IShadowVolumeSceneNode* AnimatedMeshComponent::getShadowVolumeSceneNode()
{
    return mShadowVolumeSN;
}

// Returns the current start frame number.
s32 AnimatedMeshComponent::getStartFrame() const
{
    return mAnimatedMeshSN->getStartFrame();
}

// Sets the speed (in frames/s) with which the animation is played.
void AnimatedMeshComponent::setAnimationSpeed(f32 speed)
{
    mAnimatedMeshSN->setAnimationSpeed(speed);
}

// Sets the current frame number.
void AnimatedMeshComponent::setCurrentFrame(f32 frame)
{
    mAnimatedMeshSN->setCurrentFrame(frame);
}

// Sets the frame numbers between which the animation is looped.
void AnimatedMeshComponent::setFrameLoop(s32 start, s32 end)
{
    mAnimatedMeshSN->setFrameLoop(start, end);
}

// Sets looping mode which is on by default.
void AnimatedMeshComponent::setLoopMode(bool value)
{
    mAnimatedMeshSN->setLoopMode(value);
}

// Starts a MD2 animation.
void AnimatedMeshComponent::setMD2Animation(const std::string &animationName)
{
    mAnimatedMeshSN->setMD2Animation(animationName.c_str());
}

// Starts a MD2 animation.
void AnimatedMeshComponent::setMD2Animation(EMD2_ANIMATION_TYPE anim)
{
    mAnimatedMeshSN->setMD2Animation(anim);
}

// Loads and sets a new mesh to display.
void AnimatedMeshComponent::setMesh(const std::string &fileName)
{
    // Set up variables.
    AssetGroup *assets = pParent->getAssetGroup();
    IAnimatedMesh *mesh = 0;

    // Retrieve pointer to the mesh.
    if(assets != NULL)
    {
        // Unsubscribe from previous asset.
        assets->disconnectEventSignal(std::string("meshes/") + mMeshFileName, this);

        // Get mesh.
        MeshProcessor *processor = static_cast<MeshProcessor*>(assets->getAssetProcessor("meshes"));
        mesh = processor->getMesh(fileName);

        if(mesh)
        {
           assets->connectEventSignal(std::string("meshes/") + fileName, this,
                                        &AnimatedMeshComponent::onMesh);
           mMeshFileName = fileName;
        }
    }

    else
       mesh = GameManager::Instance()->getSceneManager()->getMesh(fileName.c_str());

    // Set mesh.
    mAnimatedMeshSN->setMesh(mesh);
}

// Sets a new mesh to display.
void AnimatedMeshComponent::setMesh(IAnimatedMesh *mesh)
{
    // Check if we need to unsubscribe from an asset group mesh.
    AssetGroup *assets = pParent->getAssetGroup();

    if(assets != NULL)
    {
       assets->disconnectEventSignal(std::string("meshes/") + mMeshFileName, this);
       mMeshFileName = "";
    }

    // Set mesh.
    mAnimatedMeshSN->setMesh(mesh);
}

// Sets the mesh of the IShadowVolumeSceneNode attached to this component.
void AnimatedMeshComponent::setShadowVolumeMesh(const std::string &fileName)
{
    // Did we already create a IShadowVolumeSceneNode?
    if(mShadowVolumeSN)
    {
       // Set up variables.
       AssetGroup *assets = pParent->getAssetGroup();
       IMesh *mesh = 0;

       // Retrieve pointer to the mesh.
       if(assets != NULL)
       {
          // Unsubscribe from previous asset.
          assets->disconnectEventSignal(std::string("meshes/") + mShadowVolumeMeshFileName, this);

          // Get mesh.
          MeshProcessor *processor = static_cast<MeshProcessor*>(assets->getAssetProcessor("meshes"));
          mesh = processor->getMesh(fileName);

          if(mesh)
          {
             assets->connectEventSignal(std::string("meshes/") + fileName, this,
                                        &AnimatedMeshComponent::onShadowVolumeMesh);
             mShadowVolumeMeshFileName = fileName;
          }
       }

       else
          mesh = GameManager::Instance()->getSceneManager()->getMesh(fileName.c_str());

       // Set mesh.
       mShadowVolumeSN->setShadowMesh(mesh);
    }
}

// Sets the mesh of the IShadowVolumeSceneNode attached to this component.
void AnimatedMeshComponent::setShadowVolumeMesh(IMesh *mesh)
{
    // Check if we need to unsubscribe from an asset group mesh.
    AssetGroup *assets = pParent->getAssetGroup();

    if(assets != NULL)
    {
       assets->disconnectEventSignal(std::string("meshes/") + mShadowVolumeMeshFileName, this);
       mShadowVolumeMeshFileName = "";
    }

    // Set mesh.
    mShadowVolumeSN->setShadowMesh(mesh);
}

// Loads and sets a new IShadowVolumeSceneNode to the component.
void AnimatedMeshComponent::setShadowVolumeSceneNode(const std::string &fileName, bool zfailmethod,
                                                     f32 infinity)
{
    if(!mShadowVolumeSN)
    {
       // Create the shadow volume scene node.
       mShadowVolumeSN = mAnimatedMeshSN->addShadowVolumeSceneNode(0, pParent->getID(),
                                                                  zfailmethod, infinity);

       setShadowVolumeMesh(fileName);
    }
}

// Adds a new IShadowVolumeSceneNode to the component.
void AnimatedMeshComponent::setShadowVolumeSceneNode(IMesh *mesh, bool zfailmethod, f32 infinity)
{
    if(!mShadowVolumeSN)
    {
       mShadowVolumeSN = mAnimatedMeshSN->addShadowVolumeSceneNode(mesh, pParent->getID(),
                                                                   zfailmethod, infinity);
    }
}

// Sets the transition time in seconds.
void AnimatedMeshComponent::setTransitionTime(f32 time)
{
    mAnimatedMeshSN->setTransitionTime(time);
}

// Responds to changes of the mesh attached to this component.
void AnimatedMeshComponent::onMesh(void *p)
{
    // Get pointer to the new mesh.
    IAnimatedMesh *mesh = reinterpret_cast<IAnimatedMesh*>(p);

    // Set new mesh.
    mAnimatedMeshSN->setMesh(mesh);
}

// Responds to changes of the mesh attached to the shadow volume scene node of this component.
void AnimatedMeshComponent::onShadowVolumeMesh(void *p)
{
    // Get pointer to the new mesh.
    IMesh *mesh = reinterpret_cast<IMesh*>(p);

    // Set new mesh.
    mShadowVolumeSN->setShadowMesh(mesh);
}

// Parses for a AnimatedMeshComponent.
bool AnimatedMeshComponent::parseXML(IXMLReader *file, Entity *entity)
{
   // Forward declaration.
   AnimatedMeshComponent *component = NULL;

   // Are we dealing with a AnimatedMeshComponent?
   if(file->getNodeType() == io::EXN_ELEMENT)
   {
      if(stringw("AnimatedMeshComponent") == file->getNodeName())
      {
          // Initialise the component.
          component = new AnimatedMeshComponent(entity);
      }
   }

   if(component == NULL)
     return false;

   // Continue parsing the file.
   while(file->read())
   {
       switch(file->getNodeType())
       {
          case io::EXN_ELEMENT:

             // <animationSpeed>
             if(stringw("animationSpeed") == file->getNodeName())
               component->setAnimationSpeed(file->getAttributeValueAsFloat(L"value"));

             // <frameLoop>
             else if(stringw("frameloop") == file->getNodeName())
             {
                component->setFrameLoop(file->getAttributeValueAsInt(L"start"),
                                        file->getAttributeValueAsInt(L"end"));
             }

             // <loopMode>
             else if(stringw("loopMode") == file->getNodeName())
             {
                stringc value = file->getAttributeValue(L"value");
                component->setLoopMode( (value == "true") ? true : false );
             }

             // <MD2Animation>
             else if(stringw("MD2Animation") == file->getNodeName())
             {
                stringc value = file->getAttributeValue(L"value");
                component->setMD2Animation(value.c_str());
             }

             // <mesh>
             else if(stringw("mesh") == file->getNodeName())
             {
                stringc fileName = file->getAttributeValue(L"fileName");
                component->setMesh(fileName.c_str());
             }

             // <shadowVolumeMeshSceneNode>
             else if(stringw("shadowVolumeMeshSceneNode") == file->getNodeName())
             {
                stringc fileName = file->getAttributeValue(L"fileName");
                stringc zfailmethod = file->getAttributeValue(L"zfailmethod");

                component->setShadowVolumeSceneNode(fileName.c_str(),
                                                    (zfailmethod == "true") ? true : false,
                                                    file->getAttributeValueAsFloat(L"infinity"));
             }

             // <transitionTime>
             else if(stringw("transitionTime") == file->getNodeName())
               component->setTransitionTime(file->getAttributeValueAsFloat(L"value"));

             break;

          case io::EXN_ELEMENT_END:

             // </AnimatedMeshComponent>
             if(stringw("AnimatedMeshComponent") == file->getNodeName())
               return true;

             break;

         default:

            break;
        }

   }

   // The code should never get here.
   return false;
}

// End of File
